from typing import List
from .Class_SVDObject import SVDObject
from .Class_EnumeratedValue import EnumeratedValue
from .Class_EnumeratedValues import EnumeratedValues
from xml.dom.minidom import Document as XMLFile
import re


access_map = {
    "read-only":"read-only",# Read access is permitted. Write operations have an undefined result.
    "write-only":"write-only", # Read operations have an undefined result. Write access is permitted.
    "read-write":"read-write",# Read and write accesses are permitted. Writes affect the state of the register and reads return the register value.
    "writeOnce": "writeOnce",# Read operations have an undefined results. Only the first write after reset has an effect.
    "read-writeOnce":"read-writeOnce",# Read access is always permitted. Only the first write access after a reset will have an effect on the content. Other write operations have an undefined result.
    "ro":"read-only",
    "wo":"write-only",
    "rw":"read-write",
    "wr":"read-write",
}


class Field(SVDObject):
    def __init__(self, name: str = "", description: str = "", lsb: str = "", msb: str = "", access: str = "", dim: str = "",dimIncrement: str = "",dimIndex: str = "",modifiedWriteValues: str = "",writeConstraint: str = "",readAction: str = "",father: str = ""):
        super().__init__(name)
        self.type = "Field"
        self.father = father
        self.description = description
        self.lsb = lsb
        self.msb = msb
        self.dim = dim
        self.dimIncrement = dimIncrement
        self.dimIndex = dimIndex
        self.modifiedWriteValues = modifiedWriteValues
        self.writeConstraint = writeConstraint
        self.readAction = readAction
        self.VaildBits()
        self.fieldaccess = access.lower()
        self.enumeratedValues:EnumeratedValues = EnumeratedValues()
    def __repr__(self):
        return (f"Field(name={self.name}, type={self.type}, father={self.father}, "
                f"description={self.description}, lsb={self.lsb}, "
                f"msb={self.msb}, access={self.fieldaccess}, "
                f"enumeratedValues={self.enumeratedValues})")
    def VaildBits(self) :
        if self.msb != -1 and self.lsb != -1 :
            if self.msb < self.lsb :
                tmp = self.lsb
                self.lsb = self.msb
                self.msb = tmp

    def SetBitLow(self, lsb: str): 
        self.lsb = lsb
        self.VaildBits()
    def SetBitHigh(self, msb: str): 
        self.msb = msb
        self.VaildBits()
    def SetAccess(self, access: str): self.fieldaccess = access.lower()

    def SetFather(self, father: str):
        self.father = father
        if self.enumeratedValues :
            self.enumeratedValues.SetFather(self.myself())
    
    def SetDescription(self, description: str):
        self.description = description

    def SetEnumeratedValues(self, enumeratedValues :EnumeratedValues):
        enumeratedValues.SetFather(self.myself())
        self.enumeratedValues = enumeratedValues
        

    @classmethod
    def check_init_paremeters(cls, dic_contains_para: dict) -> str: 
        if "access" in dic_contains_para:
            access = dic_contains_para["access"].lower()
            if access in access_map.keys():
                return ""
            else :
                KnownValues =  access_map.keys()
                return f"Unknown parameter \"access\" = \"{access}\", should be in {KnownValues}!"
                
        else :
            return "does not contains parameter \"access\"!"
        
    def ValidCheck(self):        
        errcnt = super().ValidCheck()
        errcnt += self.enumeratedValues.ValidCheck();
        return errcnt
    def toString(self, tablevel) -> str:
        xml_output = (
            f"{self.TabsString(tablevel)}<field><!--{self.name}-->\n"
            f"{self.TabsString(tablevel + 1)}<name>{self.name}</name>\n"
        )
        
        if self.description :
            xml_output += f"{self.TabsString(tablevel + 1)}<description>{self.description}</description>\n"
        
        xml_output += (
            f"{self.TabsString(tablevel + 1)}<msb>{self.msb}</msb>\n"
            f"{self.TabsString(tablevel + 1)}<lsb>{self.lsb}</lsb>\n"
        )
        
        if self.fieldaccess :
            xml_output += f"{self.TabsString(tablevel + 1)}<access>{access_map[self.fieldaccess]}</access>\n"
        
        if self.enumeratedValues :
            xml_output += f"{self.enumeratedValues.toString(tablevel + 1)}"        
        
        xml_output += f"{self.TabsString(tablevel)}</field>\n"
        
        return xml_output
    def toXmlObj(self, xmlfather):
        doc = xmlfather.ownerDocument
        
        field_element = doc.createElement('field')
        
        
        if self.dim :
            dim_element = doc.createElement('dim')
            dim_text = doc.createTextNode(str(self.dim))
            dim_element.appendChild(dim_text)
            field_element.appendChild(dim_element)
        if self.dimIncrement :
            dim_element = doc.createElement('dimIncrement')
            dim_text = doc.createTextNode(str(self.dimIncrement))
            dim_element.appendChild(dim_text)
            field_element.appendChild(dim_element)
        if self.dimIndex :
            dim_element = doc.createElement('dimIndex')
            dim_text = doc.createTextNode(str(self.dimIndex))
            dim_element.appendChild(dim_text)
            field_element.appendChild(dim_element)      
            
        name_element = doc.createElement('name')
        name_text = doc.createTextNode(self.name)
        name_element.appendChild(name_text)
        field_element.appendChild(name_element)
        
        if self.description:
            description_element = doc.createElement('description')
            description_text = doc.createTextNode(self.description)
            description_element.appendChild(description_text)
            field_element.appendChild(description_element)
        
        msb_element = doc.createElement('msb')
        msb_text = doc.createTextNode(str(self.msb))
        msb_element.appendChild(msb_text)
        field_element.appendChild(msb_element)
        
        lsb_element = doc.createElement('lsb')
        lsb_text = doc.createTextNode(str(self.lsb))
        lsb_element.appendChild(lsb_text)
        field_element.appendChild(lsb_element)
        
        if self.fieldaccess:
            access_element = doc.createElement('access')
            access_text = doc.createTextNode(access_map[self.fieldaccess])
            access_element.appendChild(access_text)
            field_element.appendChild(access_element)
        
        a_element_name = 'modifiedWriteValues'
        a_element_value = self.modifiedWriteValues
        if a_element_value:
            a_element = doc.createElement(a_element_name)
            a_text = doc.createTextNode(a_element_value)
            a_element.appendChild(a_text)
            field_element.appendChild(a_element)
        
        a_element_name = 'writeConstraint'
        a_element_value = self.writeConstraint
        if a_element_value:
            wc_element = doc.createElement(a_element_name)
            
            Constraints = self.writeConstraint.split(',')
            if Constraints is not None:                
                for constraint in Constraints:
                    match = re.match(r"range\[(\d+)-(\d+)\]", constraint)
                    if match :
                        min, max = match.groups()
                        
                        r_element = doc.createElement("range")
                        
                        c_element = doc.createElement("minimum")
                        c_text = doc.createTextNode(str(min))
                        c_element.appendChild(c_text)
                        r_element.appendChild(c_element)                        
                        
                        c_element = doc.createElement("maximum")
                        c_text = doc.createTextNode(str(max))
                        c_element.appendChild(c_text)
                        r_element.appendChild(c_element)
                        
                        wc_element.appendChild(r_element)
                    
                    elif constraint == "writeAsRead" :
                        c_element = doc.createElement("writeAsRead")
                        c_text = doc.createTextNode("true")
                        c_element.appendChild(c_text)
                        wc_element.appendChild(c_element)
                    elif constraint == "useEnumeratedValues" :
                        c_element = doc.createElement("useEnumeratedValues")
                        c_text = doc.createTextNode("true")
                        c_element.appendChild(c_text)
                        wc_element.appendChild(c_element)
                    else :
                        print("[Error] Unknown constraint : ",constraint)
                        continue
            
                
                field_element.appendChild(wc_element)
                    
        
        a_element_name = 'readAction'
        a_element_value = self.readAction
        if a_element_value:
            a_element = doc.createElement(a_element_name)
            a_text = doc.createTextNode(a_element_value)
            a_element.appendChild(a_text)
            field_element.appendChild(a_element)        
                            
        if self.enumeratedValues:
            self.enumeratedValues.toXmlObj(field_element)
        
        xmlfather.appendChild(field_element)
    
    @staticmethod
    def GetTestObject():
        field = Field(
            name="TDLIS",
            description="Controls the data inversion of transmitted data (payload only).",
            lsb="1",
            msb="3",
            access="read-write"
        )
        field.SetEnumeratedValues(EnumeratedValues.GetTestObject())
        return field
    
    @staticmethod
    def TestString():
        field = Field.GetTestObject()        
        result = field.toString(6)
        print(result)